package com.asas.spaceflight.controller;

import java.util.Iterator;

import com.asas.spaceflight.model.Asteroid;
import com.asas.spaceflight.model.Bullet;
import com.asas.spaceflight.model.PhysicalEntity;
import com.asas.spaceflight.model.Player.State;
import com.asas.spaceflight.view.WorldRenderer;
import com.badlogic.gdx.math.Circle;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;

public class CollisionController {
	
	private GameEngine engine;
	
	// Auxiliary
	Array<Asteroid> hitList = new Array<Asteroid>();
	
	public CollisionController(GameEngine engine) {
		this.engine = engine;
	}
	
	public void update() {
		
		// Asteroids
		Iterator<Asteroid> itAsteroid = engine.world.getAsteroids().iterator();
		while (itAsteroid.hasNext()) {
			Asteroid obj = itAsteroid.next();
			if (hitBoundaries(obj)) {
				reapear(obj);
			}
		}

		// Bullets
		Iterator<Bullet> itBullet = engine.world.getBullets().iterator();
		while (itBullet.hasNext()) {
			Bullet obj = itBullet.next();
			if (hitBoundaries(obj) || hitLifetime(obj)) {
				itBullet.remove();
				continue;
			}
			hitList.clear();			
			if (hitAsteroid(obj, hitList)) {
				itBullet.remove(); 
				explodeAsteroids(hitList);
				continue;
			}
		}
		
		// Player
		if (hitBoundaries(engine.player)) {
			reapear(engine.player);
		}
		if (hitAsteroid(engine.player, hitList)) {
			// TEMP
			engine.player.getVelocity().set(0, 0);
			engine.player.setState(State.DYING);
		}
		// TEMP
		else if (engine.player.getState().equals(State.DYING)) {
			engine.player.setState(State.IDLE);			
		}
	}

	private Boolean hitBoundaries(PhysicalEntity entity) {
			
		float halfHeight = entity.getHeight()/2;		
		return entity.getPosition().y < -halfHeight
				|| entity.getPosition().y > WorldRenderer.HEIGHT + halfHeight
				|| entity.getPosition().x < -halfHeight
				|| entity.getPosition().x > WorldRenderer.WIDTH + halfHeight;
	}	
	private Boolean hitLifetime(PhysicalEntity entity) {
		
		return System.currentTimeMillis() >=
				entity.getBirthTime() + entity.getLifetime();
	}
	private Boolean hitAsteroid(PhysicalEntity entity, Array<Asteroid> hitList) {
					
		Iterator<Asteroid> iterator = engine.world.getAsteroids().iterator();		
		while (iterator.hasNext()) {
			Asteroid obj = iterator.next();
			if (intersectAsteroid(entity, obj)) {
				hitList.add(obj);
				return true;
			}
		}
		
		return false;
	}

	private void reapear(PhysicalEntity entity) {
			
		float halfHeight = entity.getHeight()/2;		
		if (entity.getPosition().y < -halfHeight) {
			entity.getPosition().y = WorldRenderer.HEIGHT + halfHeight;			
		}
		else if (entity.getPosition().y > WorldRenderer.HEIGHT + halfHeight) {
			entity.getPosition().y = -halfHeight;
		}
		if (entity.getPosition().x < -halfHeight) {
			entity.getPosition().x = WorldRenderer.WIDTH + halfHeight;			
		}
		else if (entity.getPosition().x > WorldRenderer.WIDTH + halfHeight) {
			entity.getPosition().x = -halfHeight;
		}
	}
	
	private void explodeAsteroids(Array<Asteroid> hitList) {

		Array<Asteroid> stardust = new Array<Asteroid>();
	
		Iterator<Asteroid> iterator = hitList.iterator();
		while (iterator.hasNext()) {
			Asteroid obj = iterator.next();
			ObjectFactory.createStardust(obj, stardust);
			engine.world.getAsteroids().addAll(stardust);
			engine.physicalController.getEntities().addAll(stardust);
		}
		
		engine.world.getAsteroids().removeAll(hitList, false);
	}
	
	private Boolean intersectAsteroid(PhysicalEntity entity, Asteroid asteroid) {

		switch (entity.getFrameType()) {		
			case Circular: {
				Circle frame = entity.getCircularFrame();			
				switch (asteroid.getFrameType()) {
				case Circular: return Intersector.overlapCircles(frame, asteroid.getCircularFrame());
				case Polygonal: return overlapCirclePolygon(frame, asteroid.getPolygonalFrame());
				}
				break;
			}
	
			case Polygonal: {
				Polygon frame = entity.getPolygonalFrame();			
				switch (asteroid.getFrameType()) {
				case Polygonal: return Intersector.overlapConvexPolygons(frame, asteroid.getPolygonalFrame());
				case Circular: return overlapCirclePolygon(asteroid.getCircularFrame(), frame);
				}
				break;
			}
		}

		return false;
	}
	
	public boolean overlapCirclePolygon(Circle circle, Polygon polygon) {
	    float []vertices=polygon.getTransformedVertices();
	    Vector2 center=new Vector2(circle.x, circle.y);
	    float squareRadius=circle.radius*circle.radius;
	    for (int i=0;i<vertices.length;i+=2){
	        if (i==0){
	            if (Intersector.intersectSegmentCircle(new Vector2(vertices[vertices.length-2], vertices[vertices.length-1]), new Vector2(vertices[i], vertices[i+1]), center, squareRadius))
	                return true;
	        } else {
	            if (Intersector.intersectSegmentCircle(new Vector2(vertices[i-2], vertices[i-1]), new Vector2(vertices[i], vertices[i+1]), center, squareRadius))
	                return true;
	        }
	    }
	    return false;
	}
}
